const vscode = require('vscode');
const ztmz_tokens_provider = require('./tokens_provider');
// const ztmz_tokens = ztmz_tokens_provider.tokens
const tokenTypes = ztmz_tokens_provider.tokenTypes
const ztmz_tokens_dict = ztmz_tokens_provider.tokenDict;
const attributeDict = ztmz_tokens_provider.attributeDict;

let unknownTokenDecorationType = vscode.window.createTextEditorDecorationType({
    isWholeLine: false,
    rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
    overviewRulerLane: vscode.OverviewRulerLane.Right,
    light: {
        fontWeight: 'bold',
        color: 'red'
    },
    dark: {
        fontWeight: 'bold',
        color: 'red'
    }
})

const UNKNOWN_TOKEN = "unknown_token";

let legend = new vscode.SemanticTokensLegend(tokenTypes);

function isNumeric(str) {
    if (typeof str != "string") return false // we only process strings!  
    return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
           !isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail
}

let unknownTokenRanges = [];

let diagnosticCollection = vscode.languages.createDiagnosticCollection('pacenote');


async function getTokenizer() {
    let tokenizer = vscode.languages.registerDocumentSemanticTokensProvider({ language: 'pacenote', scheme: 'file' }, {
        provideDocumentSemanticTokens(document) {
            let semanticBuilder = new vscode.SemanticTokensBuilder(legend);
            unknownTokenRanges = [];
            let diagnostics = [];
            // let text = document.getText();
            // const lines = text.split(/\r\n|\r|\n/);
            for (let i = 0; i < document.lineCount; i++) {
                let line = document.lineAt(i).text;
                // find # to determine comment
                let commentIndex = line.indexOf('#');
                if (commentIndex >= 0) {
                    semanticBuilder.push(new vscode.Range(i, commentIndex, i, line.length), 'comment');
                    console.log(`comment: ${line.substring(commentIndex)}, start: ${commentIndex}, end: ${line.length}, line: ${i}`);
                    line = line.substring(0, commentIndex);
                }

                // find @ to determine attribute and attribute value
                let attributeIndex = line.indexOf('@');
                let attributeColumnIndex = 0;
                if (attributeIndex >= 0) {
                    let parts = line.split(/\s+/);
                    for (let j = 0; j < parts.length; j++) {
                        let part = parts[j];
                        if (part.startsWith('@')) {
                            let attributeEnd = attributeIndex + part.length;
                            semanticBuilder.push(new vscode.Range(i, attributeIndex, i, attributeEnd), 'ATTRIBUTE');
                            console.log(`attribute: ${part}, start: ${attributeIndex}, end: ${attributeEnd}, line: ${i}`);
                            attributeColumnIndex = attributeEnd;
                            if (!attributeDict.hasOwnProperty(part.trim())) {
                                // warning
                                let range = new vscode.Range(i, attributeIndex, i, attributeEnd);
                                diagnostics.push(new vscode.Diagnostic(range, `Unknown attribute: ${part.trim()}`, vscode.DiagnosticSeverity.Warning));
                            }
                        } else {
                            let attributeValueStart = line.indexOf(part, attributeColumnIndex);
                            let attributeValueEnd = attributeValueStart + part.length;
                            semanticBuilder.push(new vscode.Range(i, attributeValueStart, i, attributeValueEnd), 'ATTRIBUTE_VALUE');
                            console.log(`attribute value: ${part}, start: ${attributeValueStart}, end: ${attributeValueEnd}, line: ${i}`);
                            attributeColumnIndex = attributeValueEnd;
                        }
                    }
                    continue;
                }

                // split by , : / \ 
                let tokens = line.split(/,|:|\/|\\/);
                let columnIndex = 0;
                for (let j = 0; j < tokens.length; j++) {
                    if (j == 0) {
                        // first token, if it's number, it's DISTANCE
                        let lineNumber = tokens[j].trim();
                        if (isNumeric(lineNumber)) {
                            semanticBuilder.push(new vscode.Range(i, 0, i, tokens[j].length), 'DISTANCE');
                            console.log(`DISTANCE: ${lineNumber}, start: 0, end: ${tokens[j].length}, line: ${i}`);
                            columnIndex = tokens[j].length;
                            continue;
                        }
                    }
                    let token = tokens[j];
                    let isUnknown = ztmz_tokens_dict[token.trim()] ? false : true;
                    let tokenType = isUnknown ? 'UNKNOWN' : ztmz_tokens_dict[token.trim()].type;
                    let tokenStart = line.indexOf(token, columnIndex);
                    let tokenEnd = tokenStart + token.length;
                    let tokenLine = i;
                    let tokenChar = j;
                    console.log(`token: ${token}, type: ${tokenType}, start: ${tokenStart}, end: ${tokenEnd}, line: ${tokenLine}, char: ${tokenChar}`);
                    columnIndex = tokenEnd;
                    let range = new vscode.Range(tokenLine, tokenStart, tokenLine, tokenEnd);
                    semanticBuilder.push(range, tokenType);

                    if (isUnknown && token.trim().length > 0) {
                        unknownTokenRanges.push(range);
                        diagnostics.push(new vscode.Diagnostic(range, `Unknown token: ${token}`, vscode.DiagnosticSeverity.Error));
                    }
                }
            }
            
            let activeEditor = vscode.window.activeTextEditor;
            activeEditor.setDecorations(unknownTokenDecorationType, unknownTokenRanges);
            diagnosticCollection.set(document.uri, diagnostics);

            return semanticBuilder.build();
        }
    }, legend);
    return tokenizer;
}

module.exports = {
    getTokenizer,
    diagnosticCollection
};